package fhq.hcmute.edu.vn.network;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.PublicKey;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Window;
import fhq.hcmute.edu.vn.activities.R;
import fhq.hcmute.edu.vn.message.MessageFactory;
import fhq.hcmute.edu.vn.message.MyMessage;
import fhq.hcmute.edu.vn.security.KeyManagement;
import fhq.hcmute.edu.vn.util.BundleUtil;
import fhq.hcmute.edu.vn.util.ProjectUtil;

public class Network extends AsyncTask<Object, Object, Void> {
	private Socket clientSocket;
	private ComputerInfos computerInfos;
	private PhoneInfos phoneInfos;
	private Message handlerMessage = new Message();
	private Bundle bundle = new Bundle();
	private ProgressDialog progressDialog;
	private Activity activity;
	private Handler handler;
	
	private static Network instance = null;

	public void destroyAsynTask() {
		instance = null;
	}

	public Activity getActivity() {
		return activity;
	}

	public void setActivity(Activity activity) {
		this.activity = activity;
	}

	public static Network getInstance() {
		if (instance == null) {
			instance = new Network();
		}
		return instance;
	}

	private Network() {
	}

	public final boolean isConnected() {
		if (clientSocket != null) {
			return clientSocket.isConnected();
		}
		return false;
	}

	public final boolean isClosed() {
		if (clientSocket != null) {
			return clientSocket.isClosed();
		}
		return false;
	}

	public final void connect() throws IOException, ClassNotFoundException {
		clientSocket =
				new Socket(computerInfos.getIpAddress(),
						computerInfos.getPort());
		clientSocket.setTcpNoDelay(true);
		clientSocket.setKeepAlive(true);
		authentication(phoneInfos);
	}

	private void authentication(final PhoneInfos phoneInfos)
			throws IOException, ClassNotFoundException {
		MyMessage myMessage = null;
		PublicKey privateKey = null;
		ObjectInputStream oin;
		byte[] decBytes = null;
		try {
			oin = new ObjectInputStream(activity.openFileInput("public.key"));
			privateKey = (PublicKey) oin.readObject();
			byte[] inpBytes = phoneInfos.toString().getBytes();
			decBytes = KeyManagement
					.encrypt(inpBytes, privateKey);
		} catch (StreamCorruptedException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			privateKey = null;
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		myMessage =
				MessageFactory.createConnectMessage(phoneInfos, decBytes);
		sendToServer(myMessage);
		myMessage = receiveFromServer();
		progressDialog.dismiss();
		if (myMessage.getType() == MessageFactory.PERMISSION) {
			handlerMessage.what = ProjectUtil.AUTHENTICATION;
			boolean isSuccess = (Boolean) myMessage.getFirstData();
			bundle.putBoolean(BundleUtil.AUTHENTICATION_KEY, isSuccess);
			sendMessageHandler(handlerMessage);
		} else if (myMessage.getType() == MessageFactory.PERMISSION_REMEMBER) {
			handlerMessage.what = ProjectUtil.AUTHENTICATION_REMEMBER;
			boolean isSuccess = (Boolean) myMessage.getFirstData();
			privateKey = (PublicKey) myMessage.get(1);
			bundle.putBoolean(BundleUtil.AUTHENTICATION_KEY, isSuccess);
			bundle.putSerializable(BundleUtil.AUTHENTICATION_PUBLIC_KEY,
					privateKey);
			sendMessageHandler(handlerMessage);
		}
	}

	public final MyMessage receiveFromServer() throws IOException,
			ClassNotFoundException {
		if (clientSocket.isConnected()) {
			ObjectInputStream ois =
					new ObjectInputStream(clientSocket.getInputStream());
			MyMessage message = (MyMessage) ois.readObject();
			return message;
		}
		return null;
	}

	public final void sendToServer(final MyMessage object) throws IOException {
		if (clientSocket.isConnected()) {
			ObjectOutputStream ous =
					new ObjectOutputStream(clientSocket.getOutputStream());
			ous.writeObject(object);
		}
	}

	@Override
	protected final void onPostExecute(final Void result) {
		super.onPostExecute(result);
		progressDialog.dismiss();
		if (hasError) {
			bundle.putString(BundleUtil.ERROR_KEY, error);
			sendMessageHandler(handlerMessage);
			progressDialog.dismiss();
			instance = null;
		}
	}

	@Override
	protected final void onPreExecute() {
		// TODO Auto-generated method stub
		super.onPreExecute();
		progressDialog =
				new ProgressDialog(activity);
		progressDialog.setCanceledOnTouchOutside(false);
		progressDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
		progressDialog.setMessage(activity.getResources()
				.getString(R.string.Dialog_Message_Connecting));
		progressDialog.show();
	}

	private boolean hasError = true;
	private String error = "";

	@Override
	protected final Void doInBackground(final Object... params) {
		this.computerInfos = (ComputerInfos) params[0];
		this.phoneInfos = (PhoneInfos) params[1];
		handlerMessage.what = ProjectUtil.CONNECTION_ERROR;
		try {
			connect();
			hasError = false;
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			error = e.getMessage();
		} catch (IOException e) {
			error = e.getMessage();
		} catch (ClassNotFoundException e) {
			error = e.getMessage();
		}
		return null;
	}

	private void sendMessageHandler(final Message message) {
		message.setData(this.bundle);
		handler.sendMessage(message);
	}

	public Handler getHandler() {
		return handler;
	}

	public void setHandler(Handler handler) {
		this.handler = handler;
	}
}
